package org.traffometer.traffobase.model;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

import org.opendmtp.codes.Encoding;
import org.opendmtp.server.base.Packet;

/**
 * Listens for packets on the specified port.  Uses Traffometer-Aqua implementation.
 * 
 * @author George Lee
 * @author Robert Brewer (original implementation)
 *
 */
public class PacketListener {

  /** Server to connect to. */
  private String serverInterface;

  /** Default server value. */
  private final String defaultServerInterface = "127.0.0.1";

  /** Port number to connect to. */
  private int port;

  /** Default port value. */
  private final int defaultServerPort = 31000;

  /**
   * Creates a new PacketListener listening on the provided interface and port, and storing any
   * received TraffoPackets in the provided PacketStore.
   * 
   * @param serverInterface interface to bind the server to (usually an IP address).
   * @param port port to bind the server to (a number between 0 and 65535 inclusive).
   */
  public PacketListener(String serverInterface, int port) {
    if (serverInterface == null || "".equals(serverInterface)) {
      this.serverInterface = defaultServerInterface;
    }
    else {
      this.serverInterface = serverInterface;
    }

    //Check for invalid server port.
    if (port <= 0 || port > 65535) {
      this.port = defaultServerPort;
    }
    else {
      this.port = port;
    }
    // Location to store packets.
    // this.packetStore = packetStore;
  }

  /**
   * Listens on the configured interface and port for connections from Traffo clients. Note that at
   * present only one TCP connection is handled, at which point the server exits. Also, no attempt
   * has been made to start a new thread for the incoming connection, which would be a good thing.
   */
  public void listen() {
    ServerSocket serverSocket = null;
    Socket socket = null;
    InputStream input;
    OutputStream output;

    try {
      serverSocket = new ServerSocket(port, 10, InetAddress.getByName(this.serverInterface));
      System.out.println("Listening on " + this.serverInterface + " on port " + this.port + ".");
      socket = serverSocket.accept();
      input = socket.getInputStream();
      output = socket.getOutputStream();
      if ((input == null) || (output == null)) {
        // Log.error(LOG_NAME, "Null socket/data");
        System.out.println("Error, null socket/data.");
        return;
      }
      handlePacketStream(input, output);
    }
    catch (IOException e) {
      // Log.error(LOG_NAME, "Exception", e);
      System.out.println("Error: " + e);
      return;
    }
    finally {
      try {
        if (serverSocket != null) {
          serverSocket.close();
        }
        if (socket != null) {
          socket.close();
        }
      }
      catch (IOException e) {
        System.out.println("Error while closing the socket: " + e);
        // Log.error(LOG_NAME, "Got error when closing server socket! " + e);
      }
    }
  }

  /**
   * Handles the input and output streams from the socket and parses it into OpenDMTP packets.
   * 
   * @param input Input stream for packet information.
   * @param output Output stream to write packet information.
   */
  public void handlePacketStream(InputStream input, OutputStream output) {
    Packet traffoPacket;
    int theByte;
    boolean isTraffoPacket = false;
    //boolean isAccountIDPacket = false;
    //boolean isDeviceIDPacket = false;
    byte[] payloadBytes;
    int packetLength = 0;

    try {
      //Go through packet stream
      for (theByte = input.read(); theByte != -1; theByte = input.read()) {
        //Check for header byte
        if (theByte != Packet.HEADER_BASIC) {
          //If the byte is not of the correct type, keep searching for it.
          continue;
        }

        //Retrieve packet type.
        theByte = input.read();
        //Packet type is 0 by default.
        if (theByte == 0) {
          isTraffoPacket = true;
        }
        else {
          //Error in retrieving packet type.  Correct type not found.
          System.out.println("Error in retrieving the encoding type.");
          return;
        }

        //Retrieve packet length.
        theByte = input.read();
        if (theByte == -1) {
          System.out.println("End of file occurred while retrieving the packet length.");
          return;
        }
        packetLength = theByte;
        payloadBytes = new byte[packetLength];

        //Retrieve the payload (the rest of the packet).
        for (int i = 0; i < packetLength - 3; i++) {
          theByte = input.read();
          //If end of file occurred, return.
          if (theByte == -1) {
            return;
          }
          payloadBytes[i] = (byte) theByte;
        }
      }
    }

    //Catch errors from reading the input stream.
    catch (IOException e) {
      System.out.println("Error while reading bytes.");
      return;
    }
  }
}
